
#include <pthread.h>
#include <dlfcn.h>

#include "net.h"
#include "flow.h"
#include "config.h"
#include "state.h"
#include "hook.h"
#include "tcp.h"

int g_use_hook = 0;
extern int8_t g_pause;
extern int8_t g_state_ready;

void* load_hook(const char* fn, p_main_flow mflow);

uint16_t fill_data(char* buf, uint16_t size) {
	return NO_ERR;
}

uint16_t check_data(char* buf, uint16_t size) {
	return NO_ERR;
}

uint8_t check_pause(void* arg)
{
	if (g_pause) return 1;
	p_main_flow mflow = (p_main_flow)arg;
	if (mflow->m_flow_state.send_limit == 0) 
		return 0;
	if (mflow->m_flow_state.tx_pkt >= mflow->m_flow_state.send_limit) {
		return 1;
	}
	return 0;
}

void* socket_thread(void* arg)
{
	p_main_flow mflow = (p_main_flow)arg;

	mflow->m_fill_data = NULL;
	mflow->m_check_data = NULL;
	//mflow->m_fill_data = fill_data;
	//mflow->m_check_data = check_data;
	mflow->m_check_pause = check_pause;
        if (mflow->m_init_flow) {
		printf("init flow...\n");
		if (mflow->m_init_flow(mflow) != NO_ERR) {
			printf("ERROR: flow init failed\n");
			goto SOCKET_EXIT;
		}
		printf("init flow end\n");
	}
	if (!g_use_hook) {
		switch (mflow->m_socket_io.flow_type) {
			case SOCKET_TCP_TYPE_SERVER:
				g_state_ready = 1;
				tcp_server(mflow);
				break;
			case SOCKET_TCP_TYPE_CLIENT:
				g_state_ready = 1;
				tcp_client(mflow);
				break;
			default:
				printf("udp\n");
				if (init_main_flow(mflow) != NO_ERR) {
					goto SOCKET_EXIT;
				}
				g_state_ready = 1;
				main_loop(mflow);
				break;
		}
	} else
		main_loop(mflow);

	if (mflow->m_clean_flow) {
		mflow->m_clean_flow(mflow);
	}
SOCKET_EXIT:
	mflow->m_bstop = 1;
	sleep(1);
	printf("socket thread exit\n");
	stop_state_monitor();
	pthread_exit(NULL);
}

static p_main_flow g_main_flow;
static pthread_t socket_th;

void except(int signo)
{
	(void)signo;
	if (g_main_flow) {
		g_main_flow->m_bstop = 1;
	}
	sleep(1);
	stop_state_monitor();
	exit(0);
}



int main(int argc, char** argv)
{
	int ret;
	void* status;
	main_flow mflow;
	memset(&mflow, 0, sizeof(main_flow));
	g_main_flow = &mflow;
	pthread_attr_t th_attr;
	struct sched_param param = {
		.sched_priority = 255
	};

	char config_fn[128] = "./cfg1.ini";
	const char* optstring = "f:L:";
	int cmd;
	char hook[128] = {0};
	while ((cmd = getopt(argc, argv, optstring)) != -1) {
		switch (cmd) {
			case 'f':
				sprintf(config_fn, optarg);
				break;
			case 'L':
				g_use_hook = 1;
				sprintf(hook, optarg);
				break;
			default:
				break;
		}
	}
	
	init_flow_state(&mflow);
	init_main_flow_config(&mflow);
	if (access(config_fn, F_OK)!=0)
	{
		printf("config not exists. use default config\n");
	} else if(load_file(config_fn) != 0) 
	{
		printf("config error\n");
		return 0;
	} else {
		parse_input_parameter(&mflow);
		printf("config ok\n");
	}
	print_main_flow_input(&mflow);

	void* handle = NULL;
	if (g_use_hook) {
		g_use_hook = 0;
		if (access(hook, F_OK)!=0) {
			printf("hook:Plugin not found (file does not exist)[%s]\n", hook);
		} else if ((handle = load_hook(hook, &mflow)) == NULL) {
			printf("hook:Plug-in loading failure\n");
		} else {
			printf("hook:usr hook\n");
			g_use_hook = 1;
		}
	}
	g_state_ready = 0;
	mflow.m_bstop = 0;
	
	pthread_attr_init(&th_attr);
	pthread_attr_setdetachstate(&th_attr, PTHREAD_CREATE_JOINABLE);
	pthread_attr_setschedpolicy(&th_attr, SCHED_RR);
	pthread_attr_setschedparam(&th_attr, &param);

	ret = pthread_create(&socket_th, &th_attr, socket_thread, &mflow);
	if (ret) {
		printf("ERROR: return code from pthread_create() is %d\n", ret);
		exit(-1);
	}
	ret = start_state_monitor(&mflow);
	if (ret) {
		printf("ERROR2: return code from pthread_create() is %d\n", ret);
		exit(-1);
	}
	signal(SIGINT, except);
	signal(SIGQUIT, except);
	signal(SIGILL, except);

	ret = pthread_join(socket_th, &status);
	stop_state_monitor();
	if (g_use_hook) {
		dlclose(handle);
	}
	return 0;
}

void* load_hook(const char* fn, p_main_flow mflow)
{
	printf("load %s...\n", fn);
	void* handle = dlopen(fn, RTLD_LAZY);
	if (!handle) {
		printf("%s\n", dlerror());
		return NULL;
	}
	uint16_t (*init)(void*) = dlsym(handle, "init_flow");
	if (!init) {
		printf("Failed to get function init_flow:%s\n", dlerror());
		return NULL;
	}
	mflow->m_init_flow = init;
	void (*clean)(void*) = dlsym(handle, "clean_flow");
	mflow->m_clean_flow = clean;
	return handle;
}






